1. Field of the Invention
The invention relates to a computer implemented method for eliminating common subexpressions while constructing a static single assignment language representation of at least a portion of source code.
2. Related Applications
The invention disclosed and claimed herein is related to the following applications:
(1) my co-pending U.S. patent application Ser. No. 08/831,074, filed herewith, entitled "A Method for Constructing a Static Single Assignment Language Accommodating Arbitrarily Complex Symbolic Memory References" (Attorney Docket No. INPA:002.vertline.P3497); PA1 (2) my co-pending U.S. patent application Ser. No. 08/831,739, filed herewith, entitled "A Method of Using Static Single Assignment to Color Out Artificial Register Dependencies" (Attorney Docket No. INPA:019.vertline.P3941); PA1 (3) my co-pending U.S. patent application Ser. No. 08/829,847, filed herewith, entitled "A Method for Identifying Partial Redundancies in Existing Processor Architectures" (Attorney Docket No. INPA:014.vertline.P3936); PA1 (4) my co-pending U.S. patent application Ser. No. 08/829,933, filed herewith, entitled "A Method for Identifying Partial Redundancies in a New Processor Architecture" (Attorney Docket No. INPA:021.vertline.P4306); PA1 (5) my co-pending U.S. patent application Ser. No. 08/831,159, filed herewith, entitled "A Method for Determining the Set of Variables that May Be Ambiguously Defined at a Point in a Computer Program" (Attorney Docket No. INPA:015.vertline.P3947); PA1 (6) my co-pending U.S. patent application Ser. No. 08/829,980, filed herewith, entitled "A Method for Optimizing a Loop in a Computer Program by Removing Loop-Invariant Loads Outside of a Loop" (Attorney Docket No. INPA:017.vertline.P3939); PA1 x=6 PA1 x=x+5 PA1 x=7 PA1 x=x+8 PA1 t.sub.1 =6 PA1 t.sub.2 =t.sub.1 +5 PA1 t.sub.3 =7 PA1 t.sub.4 =t.sub.3 +8 PA1 a=x+y PA1 b=x+y+z
All these applications are hereby expressly incorporated herein by reference for all permissible purposes as if expressly set forth vebatim herein.
3. Description of the Related Art
Most computer programmers write computer programs in source code using high-level languages such as C, FORTRAN, or PASCAL. While programmers may easily understand such languages, modem computers are not able to directly read such languages. Thus, such computer programs must be translated into a language, known as machine language, that a computer can read and execute. One step in the translating process is performed by a compiler. A compiler translates a source code program into object code. Object code is a machine language description of a source code program. Object code produced by conventional compiling techniques may often be made to execute faster. This improvement is called optimization. Compilers that apply code-improving transformations are called optimizing compilers.
Some conventional optimizing compilers translate high-level computer programs into an intermediate language known as a Static Single Assignment (SSA) representation. This SSA intermediate language is used as a basis to perform certain optimizations. After these optimizations are performed, these conventional compilers translate the SSA intermediate language into optimized object code. A deeper explanation of SSA intermediate languages follows and employs the terminology set forth immediately below.
A statement in a computer program is said to "define" a variable if it assigns, or may assign, a value to that variable. For example, the statement "x=y+z" is said to "define" x. A statement that defines a variable contains a "definition" of that variable. In this context, there are two types of variable definitions: unambiguous definitions and ambiguous definitions which may also be called complex definitions. Ambiguous definitions may also be called complex definitions.
When a definition consistently defines a particular variable, the definition is said to be an "unambiguous definition" of that variable. For example, the statement, "x=y" always assigns the value of y to x. Such a statement always defines the variable x with the value of y. Thus, the statement "x=y" is an "unambiguous definition" of x. If all definitions of a variable within particular segment of code are unambiguous definitions, then the variable is known as an unambiguous variable.
Some definitions do not consistently define the same variable. These definitions may possibly define different variables at different times in a computer program. Thus, they are called "ambiguous definitions." There are many types of ambiguous definitions and the principle common denominator among the many types is that they are not unambiguous definitions. One type of "ambiguous definition" occurs where a pointer refers to a variable. For example, the statement "*p=y" may be a definition of x since it is possible that the pointer p points to x. Thus, the definition may ambiguously define any variable x if it is possible that p points to x. In other words, *p may define one of several variables depending on the value of p. Another type of ambiguous definition is a call of a procedure with a variable passed by reference. When a variable is passed by reference, the address of the variable is passed to the procedure. Passing a variable by reference to a procedure allows the procedure to modify the variable. Still another type of ambiguous definition is a procedure that may access a variable because that variable is within the scope of the procedure. A further type of ambiguous definition occurs when a variable is not within the scope of a procedure, but the variable has been identified with another variable that is passed as a parameter or is within the scope of the procedure. Ambiguous definitions are known by those skilled in the art.
When a statement in a computer program references a variable, the statement is said to "use" the variable. For example, the statement "x=y+z" refers to and is said to "use" y and z. Similarly, y and z (but not x) are "used" in the statement "x[y]=z." A statement that uses a variable contains a "use" of that variable.
A definition of a variable "reaches" a use of that variable if that definition is the last definition of that variable prior to the use. Consider the following straight-line C pseudo code:
The definition in the first statement "x=6" reaches the use in the second statement "x=x+5." Similarly, the definition in the third statement "x=7" reaches the use in the fourth statement "x=x+8." Note that the definition in the first statement does not reach the use of the fourth statement because x is redefined in the second and third statements.
In the above example, the definitions of x in the second and third statements are said to "kill" the definition of x in the first statement in that they nullify the effects of the definition in the first statement. Only unambiguous definitions of a variable can kill other definitions of the variable. Thus, a use can be reached by both an unambiguous definition and a subsequent ambiguous definition of the same variable.
A computer programmer may address a variable by specifying the variable's location in memory. This location is known as the variable's absolute address. This method of addressing is known as direct addressing. Direct addressing commonly occurs when a variable is specified by its name. For example, in the statement "y=x," both y and x are directly addressed.
A computer programmer may also address a variable by specifying an address that refers to a different address, which may specify yet another address. This method of addressing is known as indirect addressing. Common examples of indirect addressing include pointers, arrays and combinations of pointers and arrays. Examples of indirect addressing include a[i], *p, *(p+4), **p, a[b[i]], and *(*p+4). When a variable is indirectly addressed, at least one indirect memory reference is employed to determine the absolute address of the variable.
A variable may be classified based upon the number of indirect memory references employed to determine the absolute address of the variable. For example, as discussed above, y and x may be directly addressed. Thus, there are zero indirect memory references employed to determine the absolute address of both y and x. These variables will be referred to as rank-0 variables.
A variable employing a single indirect memory reference will be referred to as a rank-1 variable. Examples of rank-1 variables include single pointer references and single array references such as a[i], *p, and *(p+4). A variable that requires two indirect memory references will be referred to as a rank-2 variable. Rank-2 variables include double pointer references and double array references and the combination of a single pointer reference and a single array reference. Examples of rank-2 variables include **p, a[b[i]], and *(*p+4). A rank-n variable employs n indirect memory references to determine the absolute address of the variable.
A definition that defines a rank-n variable will be referred to as a rank-n definition. Similarly a use of a rank-n variable will be referred to as a rank-n use. For example, the definition of the array element b[a[i]] is a rank-0 use of the variable i, a rank-1 use of the array element a[i], and a rank-2 definition of the array element b[a[i]].
Returning to the discussion of SSA intermediate languages, when a computer program is conventionally translated into a SSA intermediate language, each variable definition is given a unique name. Further, all the uses reached by that definition are also renamed to match the variable's new name. For example, consider the straight-line C pseudo code discussed above. When this C pseudo code is translated into a SSA intermediate language, the result would be the following:
The symbols t.sub.1 through t.sub.4 represent compiler temporaries. Compiler temporaries are also known by those skilled in the art as temps. Unlike most variables, temps have only a single definition. Because a temp has only a single definition, it may not be defined by an ambiguous definition. Thus, temps are unaliasable scalars. Because temps are unaliasable scalars, an expression using t.sub.1 has a different symbolic meaning from the symbolic meaning of an otherwise identical expression using i. Every use of i cannot be considered equal because i represents an aliasable variable. However, every use of t.sub.1 can be considered equal. While a compiler may not be able to determine the value contained in a temp, every use of that temp will return the same unknown value. Therefore, temps dramatically simplify certain compiler techniques.
Unlike the above straight-line C pseudo code, programs typically also contain branch statements. A branch statement is a statement that selects one set of statements from a number of alternative sets of statements. For example, consider the following if-then-else statement: ##EQU1## The flow of control through this code segment during execution will branch depending on whether p is true or false and will unite again at the statement "x=z*x." The point where the flow of control branches is known as the branch point and the point where it unites is known as the "join points" or the "confluence point."
When this C pseudo code is translated into a SSA intermediate language, the result would be the following: ##EQU2## Depending on the value of p, either t.sub.1 will be defined as 4 or t.sub.2 will be defined as 6. In order to "join" these two definitions, a special definition called a phi-function is inserted at the point where the branches join. A phi-function, which is known in the art, is a function that makes explicit a confluence point for multiple definitions that reach a particular point in a computer program.
The above phi-function contains two operands. An operand is a quantity that enters into (or results from) an operation. The operands indicate which definitions reach the join point. In this example, both t.sub.1 and t.sub.2 reach the join point. Thus, both t.sub.1 and t.sub.2 are operands to the phi-function that defines t.sub.3. As shown above, subsequent uses of x in the original program would use t.sub.3 in the corresponding SSA intermediate language.
Conventional SSA intermediate languages accommodate only rank-0 variables. Ambiguous definitions and uses reached by ambiguous definitions cannot be renamed as temps. Phi-nodes also cannot be inserted in conventional SSA intermediate languages without temps. Therefore, phi-nodes cannot conventionally be inserted in the presence of ambiguity interjected by ambiguous definitions and their uses. Thus, rank-1 and rank-2 variables are not included in conventional SSA intermediate languages. Because such intermediate languages contain only a limited amount of symbolic information, only limited optimizations may be based on such languages. Thus, in order to perform significant optimizations, numerous ad hoc techniques are employed. These conventional techniques are inefficient, incomplete, not well defined, and complex.
One common type of optimization is known as common subexpression elimination ("CSE"). A common subexpression is a subexpression within an expression that was previously computed and whose variables have not changed since the previous computation. For instance, consider the following code segment:
The first statement, or expression, defines the variable a using the subexpression x+y. The second statement, or expression, defines the variable b using the subexpression x+y+z. Both the first and second expressions use the common subexpression x+y to define their respective variables. Each time the subexpression "x+y" is used, the values assigned the variables x and y must be loaded. Thus, if the subexpression can be eliminated by replacing it with a single variable, requiring only a single load, execution time can be reduced. The cumulative effect of eliminating numerous common subexpressions can sometimes significantly reduce execution time.
However, current CSE techniques are part of the ad hoc and inefficient optimization process set forth above. In some cases, the inefficiencies offset the benefits of performing CSE. Current techniques also are not applicable to ran -1 and higher variables. Thus, there is a need for a more efficient CSE technique and a CSE technique that can be applied to rank-1 and higher variables.